/* eslint-disable @typescript-eslint/no-this-alias */
import ConfigForm from '..';
import {
  YForm,
  YInterception,
  YMapping,
  YItemConfig,
  YItemRow,
  YRule,
  YNodes,
  YEvent,
  YNodesFunction,
  YDirective,
  YIf,
  YFnStr,
  YFnBool,
  YFnDirective,
  YFnRule,
  YFnRules,
} from '../interface';
import ItemConfig from '../linkedList/ItemConfig';
import UtilsEntity from './UtilsEntity';

interface INodeOption {
  form: YForm;
  mapping: YMapping;
  components: { [key: string]: any };
  version: string | number;
  interception?: YInterception;
  createElement: Function;
}

const lifeComponents: {
  uniqueId: string;
  component: any;
}[] = [];

export default class Create extends UtilsEntity {
  constructor(private options: INodeOption, private ConfigForm: ConfigForm) {
    super(options.mapping?.formMapping?.linkPath);
  }

  get form() {
    return this.options.form;
  }

  get components() {
    return this.options.components || {};
  }

  get componentsMapping() {
    return this.options.mapping.componentMapping || {};
  }

  get modelMapping() {
    return this.options.mapping.modelMapping || {};
  }

  get placeholderMapping() {
    return this.options.mapping.placeholderMapping || {};
  }

  get createElement() {
    return this.options.createElement;
  }

  get defaultRequiredRuleMapping() {
    return this.options.mapping.defaultRequiredRuleMapping || {};
  }

  get interceptionRule() {
    return this.options.interception?.rule;
  }

  get interceptionNode() {
    return this.options.interception?.node;
  }

  get formMapping() {
    return this.options.mapping.formMapping || {};
  }

  get eventInterception() {
    return this.options.interception?.event;
  }

  get emitMapping() {
    return this.options.mapping.emitMapping || {};
  }

  get UIFormMapping() {
    return (
      this.options.mapping.UIFormMapping || {
        form: '',
        props: [],
      }
    );
  }

  get defaultValue() {
    return this.options.mapping.defaultValueMapping || {};
  }

  get isVue3() {
    return +this.options.version === 3;
  }

  init(rows: Array<YItemRow | YItemConfig>, baseComponentName = 'form') {
    const { component, attrs } = this.getBaseComponentInfo(baseComponentName);
    const nodes = this.createNodes(rows);
    return this.form.firstNode
      ? nodes[0]
      : this.createElement(
          component,
          attrs,
          this.getRightComponentContent(nodes)
        );
  }

  getFormProps(baseComponentName: string) {
    const props = (this.form as any).$props;
    // let filterKeys = ['model', 'validateOnRuleChange', 'firstNode', 'baseComponentName', 'parentName', 'name', 'config', 'rules', 'scopedData', 'created', 'mounted', 'beforeDestroy', 'validate', 'interception']
    return props;
  }

  getFormConfig(baseComponentName: string) {
    const modelName = this.formMapping?.model || 'model';
    const props = (this.form as any).$props;
    const result = this.deepMerge(
      this.form.interception.props
        ? this.form.interception.props(props, this.form)
        : props,
      { [modelName]: this.form.formData, rules: this.form.newRules }
    );
    const deepMergeResult = this.deepMerge(
      this.ConfigForm.propsInterception(result, this.form),
      { [modelName]: this.form.formData, rules: this.form.newRules }
    );
    let attrs = this.getRightAttrs(deepMergeResult);
    attrs = Object.keys(attrs).reduce(
      (total: { [key: string]: any }, key: string) => {
        if (baseComponentName === this.UIFormMapping.form) {
          const formProps = this.UIFormMapping.props;
          if (formProps.includes(key)) {
            total[key] = attrs[key];
          }
        }
        return total;
      },
      {}
    );
    attrs.ref = 'configForm';
    return attrs;
  }

  getBaseComponentInfo(baseComponentName: string) {
    return {
      component: this.getComponentInfo(baseComponentName),
      attrs: this.getFormConfig(baseComponentName),
    };
  }

  setComponentsMapping(componentsMapping: { [key: string]: string }) {
    this.options.mapping.componentMapping = {
      ...this.options.mapping.componentMapping,
      ...componentsMapping,
    };
    return this;
  }

  setComponents(components: { [key: string]: any }) {
    this.options.components = { ...this.options.components, ...components };
    const componentNameMap = Object.keys(components).reduce(
      (total: { [key: string]: string }, key: string) => {
        total[key] = key;
        total[this.toLowerCase(key)] = key;
        return total;
      },
      {}
    );
    this.setComponentsMapping(componentNameMap);
    return this;
  }

  getComponents() {
    return this.options.components || {};
  }

  // 判断是不是ItemConfig 是就进行返回
  toItemConfig(
    config:
      | YItemRow
      | YItemConfig
      | Array<YItemConfig | YItemRow>
      | string
      | number
      | boolean
  ) {
    if (typeof config !== 'object') {
      return null;
    }
    if ('type' in config) {
      return Object.assign({}, config) as YItemConfig;
    }
    return null;
  }

  lifeComponentHandler(component: any, itemConfig?: YItemConfig) {
    if (
      itemConfig &&
      itemConfig.life &&
      typeof component === 'object' &&
      !Array.isArray(component)
    ) {
      const uniqueId = itemConfig.uniqueId;
      const lastComponent = lifeComponents.find(
        (uniqueItem) => uniqueItem.uniqueId === uniqueId
      );
      if (lastComponent) {
        return lastComponent.component;
      }
      const life = itemConfig.life;
      const newComponentInfo = { ...component };
      const _this = this;
      Object.keys(life).forEach((key: string) => {
        const originHandler = component[key];
        newComponentInfo[key] = function (...args: any[]) {
          if (originHandler && typeof originHandler === 'function') {
            originHandler.call(this, ...args);
          }
          life[key](_this.form, this);
        };
      });

      if (Object.keys(life).length > 0) {
        const eventName = this.isVue3 ? 'unmount' : 'destroy';
        const originHandler =
          newComponentInfo[eventName] || component[eventName];
        newComponentInfo[eventName] = function (...args: any[]) {
          if (originHandler && typeof originHandler === 'function') {
            originHandler.call(this, ...args);
          }
          life[eventName](_this.form, this);
          const index = lifeComponents.findIndex(
            (info) => info.uniqueId === itemConfig.uniqueId
          );
          lifeComponents.splice(index, 1);
        };
      }

      lifeComponents.push({
        uniqueId: itemConfig.uniqueId,
        component: newComponentInfo,
      });
      return newComponentInfo;
    }
    return component;
  }

  // 获取组件映射表的信息
  getComponentInfo(key: string, itemConfig?: YItemConfig) {
    if (typeof key !== 'string' || !isNaN(Number(key))) {
      return key;
    }

    const name = this.componentsMapping[key];
    if (!name) {
      // 如果没有找到就进行全局查找
      const globalComponents = (this.form as any).$.appContext.components || {};
      const findResult =
        globalComponents[this.toUpperCase(key)] ||
        globalComponents[this.toLowerCase(key)];

      return findResult || key;
    }
    const components = this.getComponents();
    const component = components[name] || components[this.toUpperCase(name)];
    if (component) {
      return this.lifeComponentHandler(component, itemConfig);
    }
    console.warn(
      `【YcConfigForm】配置项【components】 中未找到 【${name}】 组件信息`
    );
    return null;
  }

  getFnParams(itemConfig: YItemConfig | YItemRow) {
    return {
      form: this.form,
      formData: this.dataBump(this.form.formData),
      config: itemConfig,
    };
  }

  // 获取formItem的配置项
  getFormItemConfig(itemConfig: YItemConfig) {
    const prop = this.formMapping?.prop || 'prop';
    const formItem = this.getConfigFile(itemConfig, 'formItem') || {};
    const { style = {} } = formItem;
    return {
      ...formItem,
      required: itemConfig.required || false,
      label: itemConfig.notLabel
        ? ''
        : itemConfig.hideLabel
        ? ' '
        : itemConfig?.label,
      [prop]: this.getConfigFile(itemConfig, 'prop'),
      class: [
        itemConfig.hideLabel && 'df-hide-label',
        itemConfig.notLabel && 'df-not-label',
        itemConfig.hide && 'df-hide-item',
        formItem.class,
      ],
      style,
      if: undefined,
      show: undefined,
    };
  }

  // 获取占位符信息
  getPlaceholder(label = '', type: string) {
    const mappingRes = this.placeholderMapping[type];
    if (mappingRes) {
      if (this.getType(mappingRes, 'string')) {
        return mappingRes + label;
      }
      if (this.getType(mappingRes, 'function')) {
        const res = mappingRes(label);
        return res;
      }
    }
    return '';
  }

  getDirectives(itemConfig: YItemConfig) {
    const dirs = itemConfig.directives || [];

    if (this.isVue3) {
      const result = dirs
        .map((dir: YDirective | YFnDirective) => {
          const dirResult =
            typeof dir === 'function' ? dir(this.form, itemConfig) : dir;
          const dirInfo = this.ConfigForm.resolveDirective(dirResult.name);
          return dirInfo
            ? [dirInfo, dirResult.value, dirResult.arg, dirResult.modifiers]
            : null;
        })
        .filter((directives) => !!directives);

      return result.length > 0 ? result : undefined;
    }
    return dirs.length > 0 ? dirs : undefined;
  }

  getConfigFile(config: YItemConfig | YItemRow, key: string) {
    if (!(key in config)) {
      return undefined;
    }
    const value = config[key];
    let result: any = value;
    if (typeof value === 'function') {
      result = value(this.getFnParams(config));
    }
    return key === 'prop' ? this.changePropName(result) : result;
  }

  bindHandler(
    bind: { [key: string]: any } = {},
    needHandler: boolean | string[] = true
  ) {
    if (!needHandler) {
      return bind;
    }
    const arrayHandler = (arr: any[]) => {
      return arr.map((item) => {
        if (this.getType(item, 'json') || this.getType(item, 'Array')) {
          return this.bindHandler(item);
        }
        return item;
      });
    };
    const functionHandler = (fn: Function) => {
      return (...args: any[]) => {
        const result = fn(...args);
        if (this.getType(result, 'json')) {
          return this.bindHandler(result);
        } else if (Array.isArray(result)) {
          return arrayHandler(result);
        }
        return result;
      };
    };

    return Object.keys(bind).reduce(
      (obj: { [key: string]: any }, key: string | number) => {
        const value = bind[key];
        if (
          Array.isArray(needHandler) &&
          !needHandler.includes(key as string)
        ) {
          return obj;
        }

        if (this.getType(value, 'json')) {
          if (value.__isConfig__) {
            obj[key] = this.nodesHandler(value);
          } else {
            obj[key] = this.bindHandler(value);
          }
        } else if (typeof value === 'function') {
          obj[key] = functionHandler(value);
        } else {
          obj[key] = value;
        }
        return obj;
      },
      {}
    );
  }

  getItemNodeConfig(
    itemConfig: YItemConfig,
    { needBindHandler }: { needBindHandler: boolean }
  ) {
    const { ref } = itemConfig;
    const bind = this.bindHandler(
      this.getConfigFile(itemConfig, 'bind') || {},
      'isHandlerBind' in itemConfig ? itemConfig.isHandlerBind : needBindHandler
    );
    const label = this.getConfigFile(itemConfig, 'label');
    const type = this.getConfigFile(itemConfig, 'type');
    const prop = this.getConfigFile(itemConfig, 'prop');
    let style = this.getConfigFile(itemConfig, 'style') || {};
    const classes = this.getConfigFile(itemConfig, 'class') || '';
    const column = this.modelMapping?.[type];
    const isShow = this.showJudge(itemConfig, itemConfig.show);
    if (!isShow) {
      if (typeof style === 'object') {
        style.display = 'none';
      } else {
        style += ';display: none;';
      }
    }
    const defaultValue =
      'value' in itemConfig
        ? this.getConfigFile(itemConfig, 'value')
        : type! in this.defaultValue
        ? this.defaultValue[type]
        : undefined;
    const value =
      !!prop &&
      prop in this.form.formData &&
      this.form.formData[prop] !== undefined
        ? this.form.formData[prop]
        : defaultValue;
    const valueObj = {
      [column || 'value']:
        typeof value === 'function'
          ? value(this.getFnParams(itemConfig))
          : value,
    };
    const attrs = Object.assign(
      {},
      bind,
      {
        placeholder:
          'placeholder' in bind
            ? bind.placeholder
            : this.getPlaceholder(label, type) || undefined,
      },
      prop ? valueObj : {},
      itemConfig.class ? { class: classes } : {},
      style ? { style } : {}
    );
    const events = this.getEvents(itemConfig);
    if (this.isVue3) {
      attrs.directives = this.getDirectives(itemConfig);
    }
    return Object.assign(ref ? { ref } : {}, this.getRightAttrs(attrs), events);
  }

  // 获取Row的配置项
  getRowConfig(itemRow: YItemRow) {
    const bind = itemRow.bind || {};
    const isShow = this.showJudge(itemRow);
    if (!isShow) {
      bind.style = bind.style || {};
      if (typeof bind.style === 'object') {
        bind.style.display = 'none';
      } else {
        bind.style += ';display:none;';
      }
    }
    return bind;
  }

  // 获取正确的属性参数
  getRightAttrs(attrs: { [key: string]: any }) {
    if (this.isVue3) {
      return attrs;
    }
    return { attrs };
  }

  // 获取正确的组件内容
  getRightComponentContent(content: any, slotInfo?: any) {
    if (this.isVue3) {
      return Object.assign(
        content
          ? {
              default() {
                return content;
              },
            }
          : {},
        slotInfo
      );
    }
    const contentIsArray = Array.isArray(content);
    if (!contentIsArray) {
      content = [content];
    }
    return content.concat(slotInfo);
  }

  // 创建节点
  createNodes(
    array: Array<
      YItemRow | YItemConfig[] | YItemConfig | string | number | boolean
    >
  ) {
    return array.reduce(
      (
        nodes: any[],
        config:
          | YItemRow
          | YItemConfig
          | Array<YItemConfig | YItemRow>
          | string
          | number
          | boolean
      ) => {
        let node: any;
        if (['string', 'number', 'boolean'].includes(typeof config)) {
          node = config;
          nodes.push(node);
        } else {
          const itemConfig = this.toItemConfig(config);
          if (itemConfig) {
            node = this.createItemNode(itemConfig);
          } else if (Array.isArray(config)) {
            node = this.createNodes(config);
          }
          if (![undefined, null].includes(node)) {
            nodes.push(node);
          }
        }
        return nodes;
      },
      []
    );
  }

  getSlotInfo(itemConfig: YItemConfig) {
    const slotsNodes = this.getItemConfigSlotsInfo(itemConfig);
    const slotName = this.getConfigFile(itemConfig, 'slotName');
    const prop = this.getConfigFile(itemConfig, 'prop');
    const templateSlotInfo = this.getSlots(slotName || prop);
    return Object.assign({}, slotsNodes, templateSlotInfo);
  }

  getFormItemSlotInfo(itemConfig: YItemConfig) {
    const obj = this.getSlotInfo(itemConfig);
    return Object.keys(obj)
      .filter((slotName: string) => slotName.indexOf(':') === 0)
      .reduce((total: { [key: string]: any }, slotName: string) => {
        total[slotName] = obj[slotName];
        return total;
      }, {});
  }

  getItemNodeSlotInfo(itemConfig: YItemConfig) {
    const obj = this.getSlotInfo(itemConfig);
    return Object.keys(obj)
      .filter((slotName: string) => slotName.indexOf(':') < 0)
      .reduce((total: { [key: string]: any }, slotName: string) => {
        total[slotName] = obj[slotName];
        return total;
      }, {});
  }

  ifJudge(
    itemConfig: YItemConfig,
    vIf: YIf[] | YFnBool | boolean | undefined = undefined
  ) {
    if (!vIf) {
      return true;
    }
    if (Array.isArray(vIf)) {
      if (vIf.length > 0) {
        return vIf
          .map((itemIf: YIf) => {
            if (typeof itemIf === 'function') {
              return itemIf(this.getFnParams(itemConfig));
            }
            const prop = this.changePropName(itemIf.prop);
            const bool = this.form.formData[prop] === itemIf.value;
            return itemIf.isReverse ? !bool : bool;
          })
          .every((b) => b);
      }
      return true;
    } else if (typeof vIf === 'function') {
      return vIf(this.getFnParams(itemConfig));
    } else if (typeof vIf === 'boolean') {
      return vIf;
    }
    return false;
  }

  showJudge(
    itemConfig: YItemConfig | YItemRow,
    vShow: YIf[] | YFnBool | boolean | undefined = undefined
  ) {
    if (vShow === undefined) {
      return true;
    }
    if (Array.isArray(vShow)) {
      if (vShow.length > 0) {
        return vShow
          .map((itemIf: YIf) => {
            if (typeof itemIf === 'function') {
              return itemIf(this.getFnParams(itemConfig));
            }
            const prop = this.changePropName(itemIf.prop);
            const bool = this.form.formData[prop] === itemIf.value;
            return itemIf.isReverse ? !bool : bool;
          })
          .every((b) => b);
      }
      return true;
    } else if (typeof vShow === 'function') {
      return vShow(this.getFnParams(itemConfig));
    } else if (typeof vShow === 'boolean') {
      return vShow;
    }
    return false;
  }

  // 创建表单元素节点
  createItemNode(itemConfig: YItemConfig) {
    const bool = this.ifJudge(itemConfig, itemConfig.if);
    if (!bool) {
      return null;
    }
    const type = this.getConfigFile(itemConfig, 'type');
    if (!type) {
      return;
    }

    const itemNode = this.getComponentInfo(
      this.getConfigFile(itemConfig, 'type'),
      itemConfig
    );
    const itemAttrs = this.getItemNodeConfig(itemConfig, {
      needBindHandler: itemNode.name !== this.ConfigForm.name,
    });

    const content = this.getChildren(itemConfig);
    const slotInfo = this.getItemNodeSlotInfo(itemConfig);
    const node = this.createElement(
      itemNode,
      itemAttrs,
      this.getRightComponentContent(content, slotInfo)
    );
    return this.ConfigForm.withDirectives(node, this.getDirectives(itemConfig));
  }

  getItemConfigSlotsInfo(itemConfig: YItemConfig) {
    if (itemConfig.slots) {
      const slots = itemConfig.slots;
      return Object.keys(slots).reduce(
        (total: { [key: string]: any }, slotName: string) => {
          total[slotName] = (scoped: any) =>
            this.nodesHandler(slots[slotName], scoped);
          return total;
        },
        {}
      );
    }
    return {};
  }

  nodesResultHandler(
    itemConfigs: Array<ItemConfig[] | ItemConfig | YNodesFunction | YItemRow>,
    scoped?: any
  ) {
    return itemConfigs.reduce(
      (itemArray: YItemConfig | Array<YItemConfig> | YItemRow, item) => {
        if (Array.isArray(item)) {
          return itemArray.concat(item);
        }

        if (typeof item === 'function') {
          const handler = item as YNodesFunction;
          const form = this.form;
          const handlerResult = handler({
            form,
            formData: this.form.formData,
            $props: (form as any).$props,
            scoped,
          });
          const result = this.interceptionNode
            ? this.interceptionNode(handlerResult, this.form)
            : handlerResult;

          if (result) {
            if (Array.isArray(result) || typeof result === 'function') {
              const items = this.nodesResultHandler(
                result as ItemConfig[]
              ) as ItemConfig[];
              return itemArray.concat(items);
            }
            itemArray.push(result);
          }
        } else {
          itemArray.push(item);
        }
        return itemArray;
      },
      []
    );
  }

  nodesHandler(nodes?: YNodes, scoped?: any): any {
    if (!nodes && nodes !== 0) {
      return null;
    }
    const type = typeof nodes;
    switch (type) {
      case 'string':
      case 'number':
        return nodes;
      case 'object':
        const isArray = Array.isArray(nodes);
        if (isArray) {
          const filterResult = (
            nodes as Array<YItemRow | YItemRow[] | YItemRow>
          ).filter((node) => node !== null && node !== undefined);
          const result = this.nodesResultHandler(filterResult);
          const nodeResult = (
            this.interceptionNode
              ? this.interceptionNode(result, this.form)
              : result
          ) as YItemConfig[];
          return this.createNodes(nodeResult);
        }
        const itemConfig = this.toItemConfig(nodes as YItemConfig);
        if (itemConfig) {
          if (typeof itemConfig.type === 'object') {
            const obj = itemConfig.type as any;
            if (obj.__v_isVNode) {
              return this.createElement(obj.type, itemConfig.bind);
            }
          }
          const result = (
            this.interceptionNode
              ? this.interceptionNode(itemConfig, this.form)
              : itemConfig
          ) as YItemConfig;
          return this.createItemNode(result);
        }
      case 'function':
        const handler = nodes as YNodesFunction;
        const form = this.form;
        const handlerResult = handler({
          form,
          formData: this.form.formData,
          $props: (form as any).$props,
          scoped,
        });
        const result = this.interceptionNode
          ? this.interceptionNode(handlerResult, this.form)
          : handlerResult;
        return this.nodesHandler(result, scoped);
      default:
        return null;
    }
  }

  getDictionary(key: string) {
    const dictionaryMap = this.form.getDictionary();
    return dictionaryMap[key];
  }

  getOptions(itemConfig: YItemConfig) {
    const bind = this.bindHandler(this.getConfigFile(itemConfig, 'bind') || {});
    const options = bind.options;
    if (options) {
      return options;
    }
    const dictionaryName = this.getConfigFile(itemConfig, 'dictionaryName');
    if (dictionaryName) {
      return this.getDictionary(dictionaryName) || [];
    }
    return [];
  }

  // 获取插槽信息
  getChildren(itemConfig: YItemConfig) {
    // 判断是否节点信息
    const hasNodes =
      itemConfig.nodes !== undefined && itemConfig.nodes !== null;
    // 获取节点信息
    const nodeInfo: any = hasNodes ? this.nodesHandler(itemConfig.nodes) : null;

    let childrenNodes: any[] = [];
    const optionType = this.getConfigFile(itemConfig, 'optionType');
    const options = this.getOptions(itemConfig);
    const optionsHandler = itemConfig.optionHandler || null;
    if (Array.isArray(options) && optionsHandler) {
      childrenNodes = optionsHandler(options).map((item) =>
        this.createItemNode({ ...item, notFormItem: true })
      );
    } else if (optionType && options) {
      childrenNodes = options.map((option: { [key: string]: any }) =>
        this.createElement(
          this.getComponentInfo(optionType),
          this.getRightAttrs(option)
        )
      );
    }
    const nodeInfoIsArray = Array.isArray(nodeInfo);
    const result = childrenNodes
      .concat(nodeInfoIsArray ? nodeInfo : [nodeInfo])
      .filter((item) => ![void 0, null].includes(item));
    return result.length ? result : null;
  }

  // 作用域插槽数据
  getSlotScopeData(prop: string) {
    return {
      form: this.form,
      prop: prop,
      formData: this.form.formData,
      value: this.form.formData[prop],
      $props: (this.form as any).$props,
    };
  }

  getRightSlotName(prop: string, slotName: string) {
    if (prop === slotName) {
      return 'default';
    }
    if (slotName.indexOf(':') > 0) {
      const names = slotName.split(':');
      const propName = names.shift();
      return propName === prop ? ':' + names.pop() : null;
    }
    const names = slotName.split('.');
    const popName = names.pop();
    return names.join('.') === prop ? popName : null;
  }

  // 获取插槽信息
  getSlots(prop = '', isTagNode = false) {
    if (!prop) {
      return {};
    }
    const $slots =
      (this.isVue3 ? (this.form as any).$slots : this.form.$scopedSlots) || {};
    const slotScopeData = this.getSlotScopeData(prop);
    if (this.isVue3) {
      const nodes = Object.keys($slots).reduce(
        (total: { [key: string]: Function }, key: string) => {
          const slotName = this.getRightSlotName(prop, key);
          if (slotName && typeof $slots[key] === 'function') {
            total[slotName] = (scoped: any) =>
              $slots[key]({ ...slotScopeData, scoped });
          }
          return total;
        },
        {}
      );
      return Object.keys(nodes).length > 0 ? nodes : undefined;
    }
    const nodes = Object.keys($slots).reduce((total: any[], key: string) => {
      if (key.indexOf(prop) === 0 && typeof $slots[key] === 'function') {
        const slotName =
          prop === key ? 'default' : key.replace(prop + '.', '') || 'default';
        const res = $slots[key](slotScopeData).map((VNode: any) => {
          VNode.data.slot = slotName;
          return VNode;
        });

        if (isTagNode) {
          total.push(res);
        } else {
          const slot = this.createElement(
            'template',
            {
              slot: slotName,
            },
            res[key]
          );
          if (slot) {
            total.push(slot);
          }
        }
      }
      return total;
    }, []);

    return nodes.length > 0 ? nodes : undefined;
  }

  getEventHandlerData(itemConfig: YItemConfig, args: any[]) {
    const prop = this.getConfigFile(itemConfig, 'prop');
    return {
      value: prop ? this.form.formData[prop] : undefined,
      formData: this.form.formData,
      ref: itemConfig.ref ? (this.form as any).$refs[prop] : undefined,
      form: this.form,
      args,
      config: itemConfig,
      prop: prop,
    };
  }

  // 获取需要绑定的事件
  getEvents(itemConfig: YItemConfig) {
    const eventMap = this.createEvents(itemConfig);
    if (this.isVue3) {
      const events = Object.assign(eventMap.nativeOn, eventMap.on);
      return Object.keys(events).reduce(
        (total: { [key: string]: Function }, eventName: string) => {
          const rightEventName =
            'on' + eventName.slice(0, 1).toUpperCase() + eventName.slice(1);
          total[rightEventName] = events[eventName];
          return total;
        },
        {}
      );
    }
    return eventMap;
  }

  // 创建事件
  createEvents(itemConfig: YItemConfig) {
    const prop =
      'prop' in itemConfig ? this.getConfigFile(itemConfig, 'prop') : '';
    const emitMapping = this.emitMapping;
    const events = itemConfig.events || [];
    const type = this.getConfigFile(itemConfig, 'type') || '';
    const eventInterception = this.eventInterception || undefined;
    const eventMapResult = events.reduce(
      (
        eventMap: {
          nativeOn: { [key: string]: Function };
          on: { [key: string]: Function };
        },
        event: YEvent
      ) => {
        const isFn =
          this.getType(event.handler, 'function') ||
          this.getType(event.handler, 'asyncfunction');
        const eventHandler = (...args: any[]) => {
          const data = this.getEventHandlerData(itemConfig, args);

          if (eventInterception && typeof eventInterception === 'function') {
            const bool = eventInterception(prop, event.name, data);
            if (!bool) {
              return console.warn(
                '【YcConfigForm】该事件已被拦截。详情请查看【$props.eventInterception】的配置信息'
              );
            }
          }
          if (isFn && event.handler) {
            event.handler(data);
          }
        };

        if (event.native) {
          eventMap.nativeOn[event.name] = (...args: any) =>
            eventHandler(...args);
        } else {
          eventMap.on[event.name] = eventHandler;
        }
        return eventMap;
      },
      {
        nativeOn: {},
        on: {},
      }
    );

    const modelTypes = Object.keys(emitMapping);
    const form = this.form;

    modelTypes.forEach((key: string) => {
      const componentName = emitMapping[key];
      if (componentName.indexOf(type || '') >= 0) {
        const method = eventMapResult.on[key];
        if (prop) {
          eventMapResult.on[key] = (...args: any[]) => {
            const res = args[0];
            const isEvent = (res || '').toString().indexOf('Event') > 0;
            let value = isEvent ? res.target.value : res;
            if (itemConfig.interception) {
              value = itemConfig.interception(value, form.formData[prop]);
            }

            form.formData[prop] = value;
            const data = this.getEventHandlerData(itemConfig, args);
            if (eventInterception && typeof eventInterception === 'function') {
              const bool = eventInterception(prop, key, data);
              if (!bool) {
                return console.warn(
                  '【YcConfigForm】该事件已被拦截。详情请查看【$props.eventInterception】的配置信息'
                );
              }
            }
            if (method) {
              method(data);
            }
          };
        } else if (prop !== '') {
          return console.warn(
            '【YcConfigForm】"' + type + '" 未找到prop值，无法进行字段赋值'
          );
        }
      }
    });

    return Object.assign({}, eventMapResult, {
      nativeOn:
        Object.keys(eventMapResult.nativeOn).length > 0
          ? eventMapResult.nativeOn
          : {},
    });
  }

  // 获取配置信息的校验规则
  getConfigRules(
    rules: { [key: string]: YRule[] },
    itemConfigs?: YItemConfig[]
  ): { [key: string]: YRule[] } {
    rules = rules || {};
    const that = this;
    const form = this.form;
    const newRules = Object.keys(rules).reduce(
      (obj: { [key: string]: YRule[] }, key: string) => {
        const newRules = ([] as YRule[]).concat(rules[key]);

        obj[that.changePropName(key)] = newRules.reduce(
          (total: YRule[], item) => {
            total.push(
              this.interceptionRule ? this.interceptionRule(item, form) : item
            );
            return total;
          },
          []
        );
        return obj;
      },
      {}
    );

    const configRules = this.getRules(itemConfigs || []);
    if (Object.keys(configRules).length <= 0) {
      return newRules;
    }

    return Object.keys(configRules).reduce(function (total, key) {
      total[key] = total[key].concat(newRules[key] || []);
      return total;
    }, configRules);
  }

  // 获取校验规则
  getRules(itemConfigs: YItemConfig[]) {
    const configs = this.getItemConfigRules(itemConfigs);
    return configs.reduce(
      (obj: { [key: string]: YRule[] }, item: YItemConfig) => {
        const required = this.getConfigFile(item, 'required') || false;

        let rules = this.getConfigFile(item, 'rules') as YRule[] | YFnRules;
        rules = (
          (typeof rules === 'function'
            ? rules(this.getFnParams(item))
            : rules) as YRule[]
        ).map((rule) =>
          typeof rule === 'function'
            ? ((rule as YFnRule)(this.getFnParams(item)) as YRule)
            : rule
        );
        const prop = this.getConfigFile(item, 'prop');
        const label = this.getConfigFile(item, 'label');
        const type = this.getConfigFile(item, 'type');
        const errorMessage = this.getConfigFile(item, 'errorMessage');
        const defaultRequiredRuleMapping = this.defaultRequiredRuleMapping;
        if (required) {
          if (defaultRequiredRuleMapping['*']) {
            rules = defaultRequiredRuleMapping['*'](
              item,
              'errorMessage' in item
                ? errorMessage
                : this.getPlaceholder(label, type || '')
            ).concat(rules);
          }
          if (defaultRequiredRuleMapping[type]) {
            rules = defaultRequiredRuleMapping[type](
              item,
              'errorMessage' in item
                ? errorMessage
                : this.getPlaceholder(label, type || '')
            ).concat(rules);
          }
        }
        obj[prop] = rules.map((rule) =>
          this.interceptionRule ? this.interceptionRule(rule, this.form) : rule
        );

        return obj;
      },
      {}
    );
  }

  getItemConfigRules(itemConfigs: YItemConfig[]) {
    return itemConfigs.filter((itemConfig) => {
      return (
        itemConfig.required ||
        (itemConfig.rules &&
          Array.isArray(itemConfig.rules) &&
          itemConfig.rules.length > 0)
      );
    });
  }
}
